#include <iostream>
#include "common.h"
#include <string>
using namespace std;


namespace QUOTE1_DAY19
{
    // 普通引用和常引用
    struct Person
    {
        int x_;
        string S_;
    };


    void log(Person & a)
    {
        cout << "Name: " << a.S_ << " years: " << a.x_ << endl;
    }


};

namespace QUOTE2_DAY19
{
    template<typename F ,typename T, typename U>
    void testFun(F f, T && t1, U && t2)
    {
        f(std::forward<T>(t1),std::forward<U>(t2));
    }

    void gu_y(int && t1, int & t2) // 接收左值和右值
    {
        cout << t1+t2 << endl;
    }

    template<typename T, typename U = int> // 仅仅是默认使用int类型，也可以更改
    void f(T && t1, U && t2)
    {
        cout << t1+t2 << endl;
    }
};


/*
     引用类型作为函数的返回类型,那其的返回值只能是左值(相当为变量)。

        返回值是引用类型的两种方式：
        （1）将其赋值给一个引用类型的变量,相当于把给返回变量做了一个引用

        （2）将返回值赋值给非引用变量，相当于把返回变量的值赋值给非引用变量。

        注意：

        （1）当函数的返回类型是引用时,返回变量不能是临时变量

        （2）不能使用引用变量接受返回类型不是引用的函数的返回值。


*/
namespace QUOTE3_DAY19
{
    int t3 = 0;
    // 返回值的类型为右值引用，可以作为万能引用
    // 参数的类型可以是万能引用
    int & add(int && t1,int && t2)
    {
        t3 = t1+t2;
        return (t3);
    }

    int sub(int && t1, int && t2)
    {
        return (t1-t2);
    }

};





int main(int argc ,char *argv[])
{


    {   __LOG__("普通引用");
        using namespace QUOTE1_DAY19;
        Person a;
        a.S_ = "Hubery";
        a.x_ = 23;
        log(a);

        __LOG__("常引用");
        int b = 89;

        const int & b1 = b;
        const int & b2 = (3+4);
        // b1 = 10; 这是错误的
        b = 78; // 这是正确的
        cout << b1 << endl;

    }


    {// 字符串字面量是一个左值，实质是一个字符串数组，字符串可以被隐式的转换为其相应的指针
        __LOG__("左右值和左右引用");

        const char * && pr  = "01234"; // 转换为const char * 为将亡值，是右值，可以用右值引用
        // const char * & pr1 = "01234"; // 不能这样，转换为const char * 为将亡值，是右值,不能左引用
        // const char (&&right)[6] = "01234"; // 数组类型为 const char [6] ,是左值
        const char (&left)[6] = "01234"; // 数组类型为 const char [6] ,是左值
        
        
        int a[4] = {1,2,3,4};
        int (&a1)[4] = a;// 类型为int [4], 左值
        int * && a2 = a; // 转换为 int *,右值
        for (int i = 0;i < 4;i++)
        {
            cout << i << ": " << a1[i] << endl;
        }
        cout << "size: " << sizeof(a1) << endl;


        int c = 10;
        int & c1 = c;
        int && c2 = 11;
        int & c3 = c1;
        int & c4 = c2;

        // int && c5 = c2;
        // int && c6 = c3;


    }

    {
        // 右值引用实现移动语义
        __LOG__("移动语义");
        int && a1 = 10;

        int a2 = 23;
        cout << "移动语义前a1: " << a1 << endl;// 10
        a1 = std::move(a2);

        cout << "移动语义前后a1: " << a1 << endl;// 23
    }

    {
        __LOG__("万能引用");
        using namespace QUOTE2_DAY19;
        int per1 = 23;
        int per2 = 34;

        cout << "左左: ";
        f(per1,per2);
        cout << "右右: ";
        f(23,34);
        cout << "左右: ";
        f(per1,34);
        
    }

    {
        __LOG__("完美转发");
        using namespace QUOTE2_DAY19;
        int per1 = 23;
        int per2 = 34;

        f(per1,per2);
        testFun(gu_y,23,per2);// 传入右值和左值
    }

    {
        __LOG__("auto &&");
        int lo = 5;

        auto && v1 = lo;//auto 为 int ，v1的类型为int &
        auto && v2 = 5;// auto 为int，v2的类型为int && 
        auto v3 = lo;// v3的类型为int

        cout << "v1: " << v1 << endl;
        cout << "v2: " << v2 << endl;
        cout << "v3: " << v3 << endl;
    }

    {
        __LOG__("自增");
        // 指令的读取基本是从左到右的因此是，先++
        int lp = 10;
        int & ko1 = ++lp;// 前置自增为左值
        int && ko2 = lp++; // 后置自增为右值

        //语义转发
        int && ko = std::move(lp);

    }

    {
        __LOG__("返回值类型是引用");
        using namespace QUOTE3_DAY19;

        int a1 = 12;
        int a2 = 34;
        int  & a3 = add(12,34);
        int a4 = add(12,34); // 返回值类型是引用，可以用变量类型来接收
        add(12,34) = a1;// 返回值类型是引用，可以作为左值
        cout << "返回值是引用: " << a3 << endl;
        cout << "返回值是引用,接收是类型: " << a4 << endl;



        int a5= sub(12,34);
        // int & a6 = sub(12,34); 错误返回值类型不是引用的不能用引用来接收

        cout << "a5: " << a5 << endl;
         


    }



    return 0;
}







